package service

import (
	"context"
	"net/http"
	"time"

	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/log"

	pb "gitee.com/danqingzhao/go-dango/api/user/customer/v1"
	"gitee.com/danqingzhao/go-dango/apps/user/biz"
	"gitee.com/danqingzhao/go-dango/pkg/common/core"
	"gitee.com/danqingzhao/go-dango/pkg/proto/settings"
)

type CustomerService struct {
	pb.UnimplementedCustomerServer
	log        *log.Helper
	ucCustomer *biz.CustomerUsecase
	ucRecord   *biz.RecordUsecase
}

func NewCustomerService(
	u *biz.CustomerUsecase,
	r *biz.RecordUsecase,
	logHelpers *settings.LogHelpers,
) *CustomerService {
	return &CustomerService{
		ucCustomer: u,
		ucRecord:   r,
		log:        logHelpers.Server,
	}
}

func (s *CustomerService) CreateCustomer(
	ctx context.Context,
	req *pb.CreateCustomerRequest,
) (*pb.CustomerReply, error) {
	im := biz.CustomerModel{
		Username: req.Username,
		Password: req.Password,
		IsActive: req.IsActive,
		IsStaff:  req.IsStaff,
		Remark:   req.Remark,
		RoleId:   req.RoleId,
	}
	om, err := s.ucCustomer.CreateCustomer(ctx, im, true)
	if err != nil {
		return nil, err
	}
	data := CustomerSchemasOut(ctx, s.log, *om)
	return &pb.CustomerReply{Code: http.StatusCreated, Message: "", Metadata: data}, nil
}

func (s *CustomerService) UpdateCustomer(
	ctx context.Context,
	req *pb.UpdateCustomerRequest,
) (*pb.CustomerReply, error) {
	im := biz.CustomerModel{
		Username: req.Username,
		Password: req.Password,
		IsActive: req.IsActive,
		IsStaff:  req.IsStaff,
		Remark:   req.Remark,
		RoleId:   req.RoleId,
	}
	om, err := s.ucCustomer.UpdateCustomerById(ctx, req.Pk, im, true)
	if err != nil {
		return nil, err
	}
	data := CustomerSchemasOut(ctx, s.log, *om)
	return &pb.CustomerReply{Code: 200, Message: "", Metadata: data}, nil
}

func (s *CustomerService) DeleteCustomer(ctx context.Context, req *pb.DeleteCustomerRequest) (*pb.CustomerReply, error) {
	if err := s.ucCustomer.DeleteCustomerById(ctx, req.Pk); err != nil {
		return nil, err
	}
	return &pb.CustomerReply{Code: http.StatusOK, Message: "", Metadata: nil}, nil
}

func (s *CustomerService) GetCustomer(ctx context.Context, req *pb.GetCustomerRequest) (*pb.CustomerReply, error) {
	om, err := s.ucCustomer.FindCustomerByID(ctx, req.Pk)
	if err != nil {
		return nil, err
	}
	data := CustomerSchemasOut(ctx, s.log, *om)
	return &pb.CustomerReply{Code: http.StatusOK, Message: "", Metadata: data}, nil
}

func (s *CustomerService) ListCustomer(
	ctx context.Context,
	req *pb.ListCustomerRequest,
) (*pb.PagCustomerReply, error) {
	page, size, query := s.filterCustomer(req)
	count, ms, err := s.ucCustomer.ListCustomer(ctx, page, size, query)
	if err != nil {
		return nil, err
	}
	mos, err := ListUserModelToSchemasOutBase(ctx, s.log, ms)
	if err != nil {
		return nil, err
	}
	rsp := pb.PagCustomerOutBase{
		Items: mos,
		Page:  int64(page),
		Pages: core.CountPages(count, int64(size)),
		Size:  int64(size),
		Total: count,
	}
	return &pb.PagCustomerReply{Code: http.StatusOK, Message: "", Metadata: &rsp}, nil
}

func (s *CustomerService) filterCustomer(
	req *pb.ListCustomerRequest,
) (int, int, map[string]any) {
	var (
		page  int            = core.DefaultPage
		size  int            = core.DefaultSize
		query map[string]any = make(map[string]any)
	)
	if req != nil {
		if req.Page > 0 {
			page = int(req.Page)
		}
		if req.Size > 0 {
			size = int(req.Size)
		}
		if req.Pk != 0 {
			query["id = ?"] = req.Pk
		}
		if req.Pks != "" {
			pks := core.StringToListUint32(req.Pks)
			if len(pks) > 1 {
				query["id in ?"] = pks
			}
		}
		if req.BeforeDataJoin != "" {
			t, err := time.Parse(time.RFC3339, req.BeforeDataJoin)
			if err == nil {
				query["data_join < ?"] = t
			}
		}
		if req.AfterDataJoin != "" {
			t, err := time.Parse(time.RFC3339, req.AfterDataJoin)
			if err == nil {
				query["data_join > ?"] = t
			}
		}
		if req.Username != "" {
			query["username like ?"] = req.Username
		}
		if req.IsActive != nil {
			query["is_active = ?"] = req.IsActive
		}
	}
	return page, size, query
}

func CustomerSchemasOutBase(
	ctx context.Context,
	logHelper *log.Helper,
	m biz.CustomerModel,
) *pb.CustomerOutBase {
	return &pb.CustomerOutBase{
		Id:       m.Id,
		Username: m.Username,
		DataJoin: m.DataJoin.String(),
		IsActive: m.IsActive,
		IsStaff:  m.IsStaff,
		Remark:   m.Remark,
		RoleId:   m.RoleId,
	}
}

func CustomerSchemasOut(
	ctx context.Context,
	logHelper *log.Helper,
	m biz.CustomerModel,
) *pb.CustomerOut {
	roleSchemas := RoleSchemasOutBase(ctx, logHelper, m.Role)
	return &pb.CustomerOut{
		Id:       m.Id,
		Username: m.Username,
		DataJoin: m.DataJoin.String(),
		IsActive: m.IsActive,
		Remark:   m.Remark,
		Role:     roleSchemas,
	}
}

func ListUserModelToSchemasOutBase(
	ctx context.Context,
	logHelper *log.Helper,
	ms []biz.CustomerModel,
) ([]*pb.CustomerOutBase, *errors.Error) {
	mso := make([]*pb.CustomerOutBase, 0, len(ms))
	if len(ms) > 0 {
		for _, m := range ms {
			mo := CustomerSchemasOutBase(ctx, logHelper, m)
			mso = append(mso, mo)
		}
	}
	return mso, nil
}
